home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2000 September / september_2000.iso / intercd / root / ^Linux / WindowMaker / WINGs / wbrowser.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-04-02  |  27.5 KB  |  1,166 lines

  1.  
  2.  
  3.  
  4.  
  5. #include "WINGsP.h"
  6. #include <math.h> /* for : double rint (double) */
  7.  
  8.  
  9.  
  10. typedef struct W_Browser {
  11.     W_Class widgetClass;
  12.     W_View *view;
  13.  
  14.     char **titles;
  15.     WMList **columns;
  16.  
  17.     short columnCount;
  18.     short usedColumnCount;           /* columns actually being used */
  19.     short minColumnWidth;
  20.  
  21.     short maxVisibleColumns;
  22.     short firstVisibleColumn;
  23.  
  24.     short titleHeight;
  25.  
  26.     short selectedColumn;
  27.  
  28.     WMSize columnSize;
  29.  
  30.  
  31.     void *clientData;
  32.     WMAction *action;
  33.     void *doubleClientData;
  34.     WMAction *doubleAction;
  35.  
  36.     WMBrowserDelegate *delegate;
  37.  
  38.     WMScroller *scroller;
  39.  
  40.     char *pathSeparator;
  41.  
  42.     struct {
  43.     unsigned int isTitled:1;
  44.     unsigned int allowMultipleSelection:1;
  45.     unsigned int hasScroller:1;
  46.  
  47.     /* */
  48.     unsigned int loaded:1;
  49.     unsigned int loadingColumn:1;
  50.     } flags;
  51. } Browser;
  52.  
  53.  
  54. #define COLUMN_SPACING     4
  55. #define TITLE_SPACING 2
  56.  
  57. #define DEFAULT_WIDTH                 305
  58. #define DEFAULT_HEIGHT                200
  59. #define DEFAULT_HAS_SCROLLER          True
  60. #define DEFAULT_TITLE_HEIGHT          20
  61. #define DEFAULT_IS_TITLED             True
  62. #define DEFAULT_MAX_VISIBLE_COLUMNS   2
  63. #define DEFAULT_SEPARATOR             "/"
  64.  
  65. #define MIN_VISIBLE_COLUMNS           1
  66. #define MAX_VISIBLE_COLUMNS           32
  67.  
  68.  
  69. #define COLUMN_IS_VISIBLE(b, c)    ((c) >= (b)->firstVisibleColumn \
  70.                 && (c) < (b)->firstVisibleColumn + (b)->maxVisibleColumns)
  71.  
  72.  
  73. static void handleEvents(XEvent *event, void *data);
  74. static void destroyBrowser(WMBrowser *bPtr);
  75.  
  76. static void setupScroller(WMBrowser *bPtr);
  77.  
  78. static void scrollToColumn(WMBrowser *bPtr, int column, Bool updateScroller);
  79.  
  80. static void paintItem(WMList *lPtr, int index, Drawable d, char *text,
  81.               int state, WMRect *rect);
  82.  
  83. static void loadColumn(WMBrowser *bPtr, int column);
  84.  
  85. static void removeColumn(WMBrowser *bPtr, int column);
  86.  
  87. static char*
  88. createTruncatedString(WMFont *font, char *text, int *textLen, int width);
  89.  
  90. static void willResizeBrowser(W_ViewDelegate*, WMView*, 
  91.                   unsigned int*, unsigned int*);
  92.  
  93. W_ViewDelegate _BrowserViewDelegate = {
  94.     NULL,
  95.     NULL,
  96.     NULL,
  97.     NULL,
  98.     willResizeBrowser
  99. };
  100.  
  101.  
  102.  
  103. WMBrowser*
  104. WMCreateBrowser(WMWidget *parent)
  105. {
  106.     WMBrowser *bPtr;
  107.     int i;
  108.  
  109.     wassertrv(parent, NULL);
  110.  
  111.     bPtr = wmalloc(sizeof(WMBrowser));
  112.     memset(bPtr, 0, sizeof(WMBrowser));
  113.  
  114.     bPtr->widgetClass = WC_Browser;
  115.  
  116.     bPtr->view = W_CreateView(W_VIEW(parent));
  117.     if (!bPtr->view) {
  118.     wfree(bPtr);
  119.     return NULL;
  120.     }
  121.     bPtr->view->self = bPtr;
  122.  
  123.     bPtr->view->delegate = &_BrowserViewDelegate;
  124.  
  125.     WMCreateEventHandler(bPtr->view, ExposureMask|StructureNotifyMask
  126.              |ClientMessageMask, handleEvents, bPtr);
  127.  
  128.     /* default configuration */
  129.     bPtr->flags.hasScroller = DEFAULT_HAS_SCROLLER;
  130.  
  131.     bPtr->titleHeight = DEFAULT_TITLE_HEIGHT;
  132.     bPtr->flags.isTitled = DEFAULT_IS_TITLED;
  133.     bPtr->maxVisibleColumns = DEFAULT_MAX_VISIBLE_COLUMNS;
  134.  
  135.     WMResizeWidget(bPtr, DEFAULT_WIDTH, DEFAULT_HEIGHT);
  136.  
  137.     bPtr->pathSeparator = wstrdup(DEFAULT_SEPARATOR);
  138.  
  139.     if (bPtr->flags.hasScroller)
  140.     setupScroller(bPtr);
  141.  
  142.     for (i=0; i<bPtr->maxVisibleColumns; i++) {
  143.     WMAddBrowserColumn(bPtr);
  144.     }
  145.     bPtr->usedColumnCount = 0;
  146.  
  147.     bPtr->selectedColumn = -1;
  148.  
  149.     return bPtr;
  150. }
  151.  
  152.  
  153. int
  154. WMGetBrowserMaxVisibleColumns(WMBrowser *bPtr)
  155. {
  156.     return bPtr->maxVisibleColumns;
  157. }
  158.  
  159.  
  160. void
  161. WMSetBrowserMaxVisibleColumns(WMBrowser *bPtr, int columns)
  162. {
  163.     int curMaxVisibleColumns;
  164.     int newFirstVisibleColumn = 0;
  165.  
  166.     assert ((int) bPtr);
  167.  
  168.     columns = (columns < MIN_VISIBLE_COLUMNS) ? MIN_VISIBLE_COLUMNS : columns;
  169.     columns = (columns > MAX_VISIBLE_COLUMNS) ? MAX_VISIBLE_COLUMNS : columns;
  170.     if (columns == bPtr->maxVisibleColumns) {
  171.         return;
  172.     }
  173.     curMaxVisibleColumns = bPtr->maxVisibleColumns;
  174.     bPtr->maxVisibleColumns = columns;
  175.     /* browser not loaded */
  176.     if (!bPtr->flags.loaded) {
  177.         if ((columns > curMaxVisibleColumns) && (columns > bPtr->columnCount)) {
  178.         int i = columns - bPtr->columnCount;
  179.         bPtr->usedColumnCount = bPtr->columnCount;
  180.         while (i--) {
  181.             WMAddBrowserColumn(bPtr);
  182.         }
  183.         bPtr->usedColumnCount = 0;
  184.     }
  185.     /* browser loaded and columns > curMaxVisibleColumns */
  186.     } else if (columns > curMaxVisibleColumns) {
  187.     if (bPtr->usedColumnCount > columns) {
  188.         newFirstVisibleColumn = bPtr->usedColumnCount - columns;
  189.     }
  190.     if (newFirstVisibleColumn > bPtr->firstVisibleColumn) {
  191.         newFirstVisibleColumn = bPtr->firstVisibleColumn;
  192.     }
  193.     if (columns > bPtr->columnCount) {
  194.         int i = columns - bPtr->columnCount;
  195.         int curUsedColumnCount = bPtr->usedColumnCount;
  196.         bPtr->usedColumnCount = bPtr->columnCount;
  197.         while (i--) {
  198.         WMAddBrowserColumn(bPtr);
  199.         }
  200.         bPtr->usedColumnCount = curUsedColumnCount;
  201.     }
  202.     /* browser loaded and columns < curMaxVisibleColumns */
  203.     } else {
  204.     newFirstVisibleColumn = bPtr->firstVisibleColumn;
  205.     if (newFirstVisibleColumn + columns >= bPtr->usedColumnCount) {
  206.         removeColumn(bPtr, newFirstVisibleColumn + columns);
  207.     }
  208.     }
  209.     WMResizeWidget(bPtr, bPtr->view->size.width, bPtr->view->size.height);
  210.     if (bPtr->flags.loaded) {
  211.     XClearArea(bPtr->view->screen->display, bPtr->view->window, 0, 0,
  212.            bPtr->view->size.width, bPtr->titleHeight, False);
  213.     scrollToColumn (bPtr, newFirstVisibleColumn, True);
  214.     }
  215. }
  216.  
  217.  
  218. int
  219. WMGetBrowserNumberOfColumns(WMBrowser *bPtr)
  220. {
  221.     return bPtr->usedColumnCount;
  222. }
  223.  
  224. void
  225. WMSetBrowserPathSeparator(WMBrowser *bPtr, char *separator)
  226. {
  227.     if (bPtr->pathSeparator)
  228.     wfree(bPtr->pathSeparator);
  229.     bPtr->pathSeparator = wstrdup(separator);
  230. }
  231.  
  232.  
  233.  
  234. static void
  235. drawTitleOfColumn(WMBrowser *bPtr, int column)
  236. {
  237.     WMScreen *scr = bPtr->view->screen;
  238.     int x;
  239.  
  240.     x=(column-bPtr->firstVisibleColumn)*(bPtr->columnSize.width+COLUMN_SPACING);
  241.  
  242.     XFillRectangle(scr->display, bPtr->view->window, WMColorGC(scr->darkGray), x, 0,
  243.            bPtr->columnSize.width, bPtr->titleHeight);
  244.     W_DrawRelief(scr, bPtr->view->window, x, 0,
  245.          bPtr->columnSize.width, bPtr->titleHeight, WRSunken);
  246.  
  247.     if (column < bPtr->usedColumnCount && bPtr->titles[column]) {
  248.     int titleLen = strlen(bPtr->titles[column]);
  249.     int widthC = bPtr->columnSize.width-8;
  250.  
  251.     if (WMWidthOfString(scr->boldFont, bPtr->titles[column], titleLen)
  252.         > widthC) {
  253.         char *titleBuf = createTruncatedString(scr->boldFont,
  254.                            bPtr->titles[column],
  255.                            &titleLen, widthC);
  256.         W_PaintText(bPtr->view, bPtr->view->window, scr->boldFont, x,
  257.             (bPtr->titleHeight-WMFontHeight(scr->boldFont))/2,
  258.             bPtr->columnSize.width, WACenter, WMColorGC(scr->white),
  259.             False, titleBuf, titleLen);
  260.         free (titleBuf);
  261.     } else {
  262.         W_PaintText(bPtr->view, bPtr->view->window, scr->boldFont, x,
  263.             (bPtr->titleHeight-WMFontHeight(scr->boldFont))/2,
  264.             bPtr->columnSize.width, WACenter, WMColorGC(scr->white),
  265.             False, bPtr->titles[column], titleLen);
  266.     }
  267.     }
  268. }
  269.  
  270.  
  271. WMList*
  272. WMGetBrowserListInColumn(WMBrowser *bPtr, int column)
  273. {
  274.     if (column < 0 || column >= bPtr->usedColumnCount)
  275.     return NULL;
  276.  
  277.     return bPtr->columns[column];
  278. }
  279.  
  280.  
  281. void
  282. WMSetBrowserDelegate(WMBrowser *bPtr, WMBrowserDelegate *delegate)
  283. {
  284.     bPtr->delegate = delegate;
  285. }
  286.  
  287.  
  288. int
  289. WMGetBrowserFirstVisibleColumn(WMBrowser *bPtr)
  290. {
  291.     return bPtr->firstVisibleColumn;
  292. }
  293.  
  294.  
  295. static void
  296. removeColumn(WMBrowser *bPtr, int column)
  297. {
  298.     int i, clearEnd, destroyEnd;
  299.     WMList **clist;
  300.     char **tlist;
  301.  
  302.     assert ((int) bPtr);
  303.  
  304.     column = (column < 0) ? 0 : column;
  305.     if (column >= bPtr->columnCount) {
  306.     return;
  307.     }
  308.     if (column < bPtr->maxVisibleColumns) {
  309.     clearEnd = bPtr->maxVisibleColumns;
  310.     destroyEnd = bPtr->columnCount;
  311.     bPtr->columnCount = bPtr->maxVisibleColumns;
  312.     } else {
  313.     clearEnd = column;
  314.     destroyEnd = bPtr->columnCount;
  315.     bPtr->columnCount = column;
  316.     }
  317.     if (column < bPtr->usedColumnCount) {
  318.     bPtr->usedColumnCount = column;
  319.     }
  320.     for (i=column; i < clearEnd; i++) {
  321.     if (bPtr->titles[i]) {
  322.         wfree(bPtr->titles[i]);
  323.         bPtr->titles[i] = NULL;
  324.     }
  325.     WMClearList(bPtr->columns[i]);
  326.     }
  327.     for (;i < destroyEnd; i++) {
  328.     if (bPtr->titles[i]) {
  329.         wfree(bPtr->titles[i]);
  330.         bPtr->titles[i] = NULL;
  331.     }
  332.         WMRemoveNotificationObserverWithName(bPtr,
  333.                                              WMListSelectionDidChangeNotification,
  334.                          bPtr->columns[i]);
  335.     WMDestroyWidget(bPtr->columns[i]);
  336.     bPtr->columns[i] = NULL;
  337.     }
  338.     clist = wmalloc(sizeof(WMList*) * (bPtr->columnCount));
  339.     tlist = wmalloc(sizeof(char*) * (bPtr->columnCount));
  340.     memcpy(clist, bPtr->columns, sizeof(WMList*) * (bPtr->columnCount));
  341.     memcpy(tlist, bPtr->titles, sizeof(char*) * (bPtr->columnCount));
  342.     wfree(bPtr->titles);
  343.     wfree(bPtr->columns);
  344.     bPtr->titles = tlist;
  345.     bPtr->columns = clist;
  346. }
  347.  
  348.  
  349. WMListItem*
  350. WMGetBrowserSelectedItemInColumn(WMBrowser *bPtr, int column)
  351. {
  352.     if ((column < 0) || (column >= bPtr->usedColumnCount))
  353.     return NULL;
  354.  
  355.     return WMGetListSelectedItem(bPtr->columns[column]);
  356. }
  357.  
  358.  
  359.  
  360. int
  361. WMGetBrowserSelectedColumn(WMBrowser *bPtr)
  362. {
  363.     return bPtr->selectedColumn;
  364. }
  365.  
  366.  
  367. int
  368. WMGetBrowserSelectedRowInColumn(WMBrowser *bPtr, int column)
  369. {
  370.     if (column >= 0 && column < bPtr->columnCount) {
  371.     return WMGetListSelectedItemRow(bPtr->columns[column]);
  372.     } else {
  373.     return -1;
  374.     }
  375. }
  376.  
  377.  
  378. void
  379. WMSetBrowserColumnTitle(WMBrowser *bPtr, int column, char *title)
  380. {
  381.     assert(column >= 0);
  382.     assert(column < bPtr->usedColumnCount);
  383.  
  384.     if (bPtr->titles[column])
  385.     wfree(bPtr->titles[column]);
  386.  
  387.     bPtr->titles[column] = wstrdup(title);
  388.  
  389.     if (COLUMN_IS_VISIBLE(bPtr, column) && bPtr->flags.isTitled) {
  390.     drawTitleOfColumn(bPtr, column);
  391.     }
  392. }
  393.  
  394.  
  395. void
  396. WMSetBrowserTitled(WMBrowser *bPtr, Bool flag)
  397. {
  398.     int i;
  399.     int columnX, columnY;
  400.  
  401.     if (bPtr->flags.isTitled == flag)
  402.     return;
  403.  
  404.     columnX = 0;
  405.  
  406.     if (!bPtr->flags.isTitled) {
  407.     columnY = TITLE_SPACING + bPtr->titleHeight;
  408.  
  409.     bPtr->columnSize.height -= columnY;
  410.  
  411.     for (i=0; i<bPtr->columnCount; i++) {
  412.         WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
  413.                bPtr->columnSize.height);
  414.  
  415.         columnX = WMWidgetView(bPtr->columns[i])->pos.x;
  416.  
  417.         WMMoveWidget(bPtr->columns[i], columnX, columnY);
  418.     }
  419.     } else {
  420.     bPtr->columnSize.height += TITLE_SPACING + bPtr->titleHeight;
  421.  
  422.     for (i=0; i<bPtr->columnCount; i++) {
  423.         WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
  424.                bPtr->columnSize.height);
  425.  
  426.         columnX = WMWidgetView(bPtr->columns[i])->pos.x;
  427.  
  428.         WMMoveWidget(bPtr->columns[i], columnX, 0);
  429.     }
  430.     }
  431.  
  432.     bPtr->flags.isTitled = flag;
  433. }
  434.  
  435.  
  436. void
  437. WMSortBrowserColumn(WMBrowser *bPtr, int column)
  438. {
  439.     WMSortListItems(bPtr->columns[column]);
  440. }
  441.  
  442.  
  443. void
  444. WMSortBrowserColumnWithComparer(WMBrowser *bPtr, int column,
  445.                 int (f)(const void*, const void*))
  446. {
  447.     WMSortListItemsWithComparer(bPtr->columns[column], f);
  448. }
  449.  
  450.  
  451.  
  452. WMListItem*
  453. WMInsertBrowserItem(WMBrowser *bPtr, int column, int row, char *text,
  454.             Bool isBranch)
  455. {
  456.     WMListItem *item;
  457.  
  458.     if (column < 0 || column >= bPtr->columnCount)
  459.     return NULL;
  460.     
  461.     item = WMInsertListItem(bPtr->columns[column], row, text);
  462.     item->isBranch = isBranch;
  463.  
  464.     return item;
  465. }
  466.  
  467.  
  468.  
  469.  
  470. static void
  471. willResizeBrowser(W_ViewDelegate *self, WMView *view, 
  472.           unsigned int *width, unsigned int *height)
  473. {
  474.     WMBrowser *bPtr = (WMBrowser*)view->self;
  475.     int cols = bPtr->maxVisibleColumns;
  476.     int colX, colY;
  477.     int i;
  478.  
  479.     assert(*width > 0);
  480.     assert(*height > 0);
  481.  
  482.     bPtr->columnSize.width = (*width-(cols-1)*COLUMN_SPACING) / cols;
  483.     bPtr->columnSize.height = *height;
  484.  
  485.     if (bPtr->flags.isTitled) {
  486.     colY = TITLE_SPACING + bPtr->titleHeight;
  487.     bPtr->columnSize.height -= colY;
  488.     } else {
  489.     colY = 0;
  490.     }
  491.  
  492.     if (bPtr->flags.hasScroller) {
  493.     bPtr->columnSize.height -= SCROLLER_WIDTH + 4;
  494.  
  495.     if (bPtr->scroller) {
  496.         WMResizeWidget(bPtr->scroller, *width-2, 1);
  497.         WMMoveWidget(bPtr->scroller, 1, *height-SCROLLER_WIDTH-1);
  498.     }
  499.     }
  500.  
  501.     colX = 0;
  502.     for (i = 0; i < bPtr->columnCount; i++) {
  503.     WMResizeWidget(bPtr->columns[i], bPtr->columnSize.width,
  504.                bPtr->columnSize.height);
  505.  
  506.     WMMoveWidget(bPtr->columns[i], colX, colY);
  507.  
  508.     if (COLUMN_IS_VISIBLE(bPtr, i)) {
  509.         colX += bPtr->columnSize.width+COLUMN_SPACING;
  510.     }
  511.     }
  512. }
  513.  
  514.  
  515. static void
  516. paintItem(WMList *lPtr, int index, Drawable d, char *text, int state,
  517.       WMRect *rect)
  518. {
  519.     WMView *view = W_VIEW(lPtr);
  520.     W_Screen *scr = view->screen;
  521.     int width, height, x, y;
  522.  
  523.     width = rect->size.width;
  524.     height = rect->size.height;
  525.     x = rect->pos.x;
  526.     y = rect->pos.y;
  527.  
  528.     if (state & WLDSSelected)
  529.     XFillRectangle(scr->display, d, WMColorGC(scr->white), x, y,
  530.                width, height);
  531.     else
  532.     XClearArea(scr->display, d, x, y, width, height, False);
  533.  
  534.     if (text) {
  535.         /* Avoid overlaping... */
  536.         WMFont *font = (state & WLDSIsBranch) ? scr->boldFont : scr->normalFont;
  537.     int textLen = strlen(text);
  538.     int widthC = (state & WLDSIsBranch) ? width-20 : width-8;
  539.     if (WMWidthOfString(font, text, textLen) > widthC) {
  540.         char *textBuf = createTruncatedString(font, text, &textLen, widthC);
  541.             W_PaintText(view, d, font,  x+4, y, widthC,
  542.                 WALeft, WMColorGC(scr->black), False, textBuf, textLen);
  543.         wfree(textBuf);
  544.     } else {
  545.               W_PaintText(view, d, font,  x+4, y, widthC,
  546.                 WALeft, WMColorGC(scr->black), False, text, textLen);
  547.     }
  548.     }
  549.  
  550.     if (state & WLDSIsBranch) {
  551.     XDrawLine(scr->display, d, WMColorGC(scr->darkGray), x+width-11, y+3,
  552.           x+width-6, y+height/2);
  553.     if (state & WLDSSelected)
  554.         XDrawLine(scr->display, d,WMColorGC(scr->gray), x+width-11, y+height-5,
  555.               x+width-6, y+height/2);
  556.     else
  557.         XDrawLine(scr->display, d,WMColorGC(scr->white), x+width-11, y+height-5,
  558.               x+width-6, y+height/2);
  559.     XDrawLine(scr->display, d, WMColorGC(scr->black), x+width-12, y+3,
  560.           x+width-12, y+height-5);
  561.     }
  562. }
  563.  
  564.  
  565. static void
  566. scrollCallback(WMWidget *scroller, void *self)
  567. {
  568.     WMBrowser *bPtr = (WMBrowser*)self;
  569.     WMScroller *sPtr = (WMScroller*)scroller;
  570.     int newFirst;
  571. #define LAST_VISIBLE_COLUMN  bPtr->firstVisibleColumn+bPtr->maxVisibleColumns
  572.  
  573.     switch (WMGetScrollerHitPart(sPtr)) {
  574.      case WSDecrementLine:
  575.     if (bPtr->firstVisibleColumn > 0) {
  576.         scrollToColumn(bPtr, bPtr->firstVisibleColumn-1, True);
  577.     }
  578.     break;
  579.  
  580.      case WSDecrementPage:
  581.     if (bPtr->firstVisibleColumn > 0) {
  582.         newFirst = bPtr->firstVisibleColumn - bPtr->maxVisibleColumns;
  583.  
  584.         scrollToColumn(bPtr, newFirst, True);
  585.     }
  586.     break;
  587.  
  588.  
  589.      case WSIncrementLine:
  590.     if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
  591.         scrollToColumn(bPtr, bPtr->firstVisibleColumn+1, True);
  592.     }
  593.     break;
  594.  
  595.      case WSIncrementPage:
  596.     if (LAST_VISIBLE_COLUMN < bPtr->usedColumnCount) {
  597.         newFirst = bPtr->firstVisibleColumn + bPtr->maxVisibleColumns;
  598.  
  599.         if (newFirst+bPtr->maxVisibleColumns >= bPtr->columnCount)
  600.         newFirst = bPtr->columnCount - bPtr->maxVisibleColumns;
  601.  
  602.         scrollToColumn(bPtr, newFirst, True);
  603.     }
  604.     break;
  605.  
  606.      case WSKnob:
  607.     {
  608.         double floatValue;
  609.         double value = bPtr->columnCount - bPtr->maxVisibleColumns;
  610.  
  611.         floatValue = WMGetScrollerValue(bPtr->scroller);
  612.  
  613.         floatValue = (floatValue*value)/value;
  614.  
  615.         newFirst = rint(floatValue*(float)(bPtr->columnCount - bPtr->maxVisibleColumns));
  616.  
  617.         if (bPtr->firstVisibleColumn != newFirst)
  618.         scrollToColumn(bPtr, newFirst, False);
  619. /*        else
  620.         WMSetScrollerParameters(bPtr->scroller, floatValue,
  621.                     bPtr->maxVisibleColumns/(float)bPtr->columnCount);
  622.  */
  623.  
  624.     }
  625.     break;
  626.  
  627.      case WSKnobSlot:
  628.      case WSNoPart:
  629.     /* do nothing */
  630.     break;
  631.     }
  632. #undef LAST_VISIBLE_COLUMN
  633. }
  634.  
  635.  
  636. static void
  637. setupScroller(WMBrowser *bPtr)
  638. {
  639.     WMScroller *sPtr;
  640.     int y;
  641.  
  642.     y = bPtr->view->size.height - SCROLLER_WIDTH - 1;
  643.  
  644.     sPtr = WMCreateScroller(bPtr);
  645.     WMSetScrollerAction(sPtr, scrollCallback, bPtr);
  646.     WMMoveWidget(sPtr, 1, y);
  647.     WMResizeWidget(sPtr, bPtr->view->size.width-2, SCROLLER_WIDTH);
  648.  
  649.     bPtr->scroller = sPtr;
  650.  
  651.     WMMapWidget(sPtr);
  652. }
  653.  
  654.  
  655. void
  656. WMSetBrowserAction(WMBrowser *bPtr, WMAction *action, void *clientData)
  657. {
  658.     bPtr->action = action;
  659.     bPtr->clientData = clientData;
  660. }
  661.  
  662.  
  663. void
  664. WMSetBrowserDoubleAction(WMBrowser *bPtr, WMAction *action, void *clientData)
  665. {
  666.     bPtr->doubleAction = action;
  667.     bPtr->doubleClientData = clientData;
  668. }
  669.  
  670.  
  671. void
  672. WMSetBrowserHasScroller(WMBrowser *bPtr, int hasScroller)
  673. {
  674.     bPtr->flags.hasScroller = hasScroller;
  675. }
  676.  
  677.  
  678.  
  679. char*
  680. WMSetBrowserPath(WMBrowser *bPtr, char *path)
  681. {
  682.     int i;
  683.     char *str = wstrdup(path);
  684.     char *tmp, *retPtr = NULL;
  685.     int item;
  686.     WMListItem *listItem;
  687.  
  688.     /* WMLoadBrowserColumnZero must be call first */
  689.     if (!bPtr->flags.loaded) {
  690.     return False;
  691.     }
  692.  
  693.     removeColumn(bPtr, 1);
  694.  
  695.     WMSelectListItem(bPtr->columns[0], -1);
  696.     WMSetListPosition(bPtr->columns[0], 0);
  697.  
  698.     i = 0;
  699.     tmp = strtok(str, bPtr->pathSeparator);
  700.     while (tmp) {
  701.     /* select it in the column */
  702.         item = WMFindRowOfListItemWithTitle(bPtr->columns[i], tmp);
  703.     if (item<0) {
  704.             retPtr = &path[(int)(tmp - str)];
  705.         break;
  706.     }
  707.     WMSelectListItem(bPtr->columns[i], item);
  708.     WMSetListPosition(bPtr->columns[i], item);
  709.  
  710.     listItem = WMGetListItem(bPtr->columns[i], item);
  711.     if (!listItem || !listItem->isBranch) {
  712.         break;
  713.     }
  714.  
  715.     /* load next column */
  716.     WMAddBrowserColumn(bPtr);
  717.  
  718.     loadColumn(bPtr, i+1);
  719.  
  720.     tmp = strtok(NULL, bPtr->pathSeparator);
  721.  
  722.     i++;
  723.     }
  724.     wfree(str);
  725.  
  726.     for (i = bPtr->usedColumnCount - 1;
  727.          (i > -1) && !WMGetListSelectedItem(bPtr->columns[i]);
  728.      i--);
  729.  
  730.     bPtr->selectedColumn = i;
  731.  
  732.     if (bPtr->columnCount < bPtr->maxVisibleColumns) {
  733.     int i = bPtr->maxVisibleColumns - bPtr->columnCount;
  734.     int curUsedColumnCount = bPtr->usedColumnCount;
  735.     bPtr->usedColumnCount = bPtr->columnCount;
  736.     while (i--) {
  737.         WMAddBrowserColumn(bPtr);
  738.     }
  739.     bPtr->usedColumnCount = curUsedColumnCount;
  740.     }
  741.  
  742.     scrollToColumn(bPtr, bPtr->columnCount - bPtr->maxVisibleColumns, True);
  743.  
  744.     return retPtr;
  745. }
  746.  
  747.  
  748. char*
  749. WMGetBrowserPath(WMBrowser *bPtr)
  750. {
  751.     return WMGetBrowserPathToColumn(bPtr, bPtr->columnCount);
  752. }
  753.  
  754.  
  755.  
  756. char*
  757. WMGetBrowserPathToColumn(WMBrowser *bPtr, int column)
  758. {
  759.     int i, size;
  760.     char *path;
  761.     WMListItem *item;
  762.  
  763.     if (column >= bPtr->usedColumnCount)
  764.     column = bPtr->usedColumnCount-1;
  765.  
  766.     if (column < 0) {
  767.         return wstrdup(bPtr->pathSeparator);
  768.     }
  769.  
  770.     /* calculate size of buffer */
  771.     size = 0;
  772.     for (i = 0; i <= column; i++) {
  773.     item = WMGetListSelectedItem(bPtr->columns[i]);
  774.     if (!item)
  775.         break;
  776.     size += strlen(item->text);
  777.     }
  778.  
  779.     /* get the path */
  780.     path = wmalloc(size+(column+1)*strlen(bPtr->pathSeparator)+1);
  781.     /* ignore first / */
  782.     *path = 0;
  783.     for (i = 0; i <= column; i++) {
  784.     strcat(path, bPtr->pathSeparator);
  785.     item = WMGetListSelectedItem(bPtr->columns[i]);
  786.     if (!item)
  787.         break;
  788.     strcat(path, item->text);
  789.     }
  790.  
  791.     return path;
  792. }
  793.  
  794.  
  795. static void
  796. loadColumn(WMBrowser *bPtr, int column)
  797. {
  798.     assert(bPtr->delegate);
  799.     assert(bPtr->delegate->createRowsForColumn);
  800.  
  801.     bPtr->flags.loadingColumn = 1;
  802.     (*bPtr->delegate->createRowsForColumn)(bPtr->delegate, bPtr, column,
  803.                        bPtr->columns[column]);
  804.     bPtr->flags.loadingColumn = 0;
  805.  
  806.     if (bPtr->delegate->titleOfColumn) {
  807.     char *title;
  808.  
  809.     title = (*bPtr->delegate->titleOfColumn)(bPtr->delegate, bPtr, column);
  810.  
  811.     if (bPtr->titles[column])
  812.         wfree(bPtr->titles[column]);
  813.  
  814.     bPtr->titles[column] = wstrdup(title);
  815.  
  816.         if (COLUMN_IS_VISIBLE(bPtr, column) && bPtr->flags.isTitled) {
  817.             drawTitleOfColumn(bPtr, column);
  818.         }
  819.     }
  820. }
  821.  
  822.  
  823. static void
  824. paintBrowser(WMBrowser *bPtr)
  825. {
  826.     int i;
  827.  
  828.     if (!bPtr->view->flags.mapped)
  829.     return;
  830.  
  831.     W_DrawRelief(bPtr->view->screen, bPtr->view->window, 0,
  832.          bPtr->view->size.height-SCROLLER_WIDTH-2,
  833.          bPtr->view->size.width, 22, WRSunken);
  834.  
  835.     if (bPtr->flags.isTitled) {
  836.     for (i=0; i<bPtr->maxVisibleColumns; i++) {
  837.         drawTitleOfColumn(bPtr, i+bPtr->firstVisibleColumn);
  838.     }
  839.     }
  840. }
  841.  
  842.  
  843. static void
  844. handleEvents(XEvent *event, void *data)
  845. {
  846.     WMBrowser *bPtr = (WMBrowser*)data;
  847.  
  848.     CHECK_CLASS(data, WC_Browser);
  849.  
  850.  
  851.     switch (event->type) {
  852.      case Expose:
  853.     paintBrowser(bPtr);
  854.     break;
  855.  
  856.      case DestroyNotify:
  857.     destroyBrowser(bPtr);
  858.     break;
  859.  
  860.     }
  861. }
  862.  
  863.  
  864.  
  865. static void
  866. scrollToColumn(WMBrowser *bPtr, int column, Bool updateScroller)
  867. {
  868.     int i;
  869.     int x;
  870.     int notify = 0;
  871.  
  872.  
  873.     if (column != bPtr->firstVisibleColumn) {
  874.     notify = 1;
  875.     }
  876.  
  877.     if (column < 0)
  878.     column = 0;
  879.  
  880.     if (notify && bPtr->delegate && bPtr->delegate->willScroll) {
  881.     (*bPtr->delegate->willScroll)(bPtr->delegate, bPtr);
  882.     }
  883.  
  884.     x = 0;
  885.     bPtr->firstVisibleColumn = column;
  886.     for (i = 0; i < bPtr->columnCount; i++) {
  887.     if (COLUMN_IS_VISIBLE(bPtr, i)) {
  888.         WMMoveWidget(bPtr->columns[i], x,
  889.              WMWidgetView(bPtr->columns[i])->pos.y);
  890.         if (!WMWidgetView(bPtr->columns[i])->flags.realized)
  891.         WMRealizeWidget(bPtr->columns[i]);
  892.         WMMapWidget(bPtr->columns[i]);
  893.         x += bPtr->columnSize.width + COLUMN_SPACING;
  894.     } else {
  895.         WMUnmapWidget(bPtr->columns[i]);
  896.     }
  897.     }
  898.  
  899.     /* update the scroller */
  900.     if (updateScroller) {
  901.     if (bPtr->columnCount > bPtr->maxVisibleColumns) {
  902.         float value, proportion;
  903.  
  904.         value = bPtr->firstVisibleColumn
  905.         /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
  906.         proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
  907.         WMSetScrollerParameters(bPtr->scroller, value, proportion);
  908.     } else {
  909.         WMSetScrollerParameters(bPtr->scroller, 0, 1);
  910.     }
  911.     }
  912.  
  913.     if (bPtr->view->flags.mapped)
  914.     paintBrowser(bPtr);
  915.  
  916.     if (notify && bPtr->delegate && bPtr->delegate->didScroll) {
  917.     (*bPtr->delegate->didScroll)(bPtr->delegate, bPtr);
  918.     }
  919. }
  920.  
  921.  
  922. static void
  923. listCallback(void *self, void *clientData)
  924. {
  925.     WMBrowser *bPtr = (WMBrowser*)clientData;
  926.     WMList *lPtr = (WMList*)self;
  927.     WMListItem *item;
  928.     static WMListItem *oldItem = NULL;
  929.     int i;
  930.  
  931.     item = WMGetListSelectedItem(lPtr);
  932.     if (!item) {
  933.         oldItem = item;
  934.         return;
  935.     }
  936.  
  937.     if (oldItem != item) {
  938.         for (i=0; i<bPtr->columnCount; i++) {
  939.             if (lPtr == bPtr->columns[i])
  940.                 break;
  941.         }
  942.         assert(i<bPtr->columnCount);
  943.  
  944.     bPtr->selectedColumn = i;
  945.  
  946.         /* columns at right must be cleared */
  947.         removeColumn(bPtr, i+1);
  948.         /* open directory */
  949.         if (item->isBranch) {
  950.             WMAddBrowserColumn(bPtr);
  951.         }
  952.         if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
  953.             i = 0;
  954.         else
  955.             i = bPtr->usedColumnCount-bPtr->maxVisibleColumns;
  956.         scrollToColumn(bPtr, i, True);
  957.     if (item->isBranch) {
  958.             loadColumn(bPtr, bPtr->usedColumnCount-1);
  959.     }
  960.     }
  961.  
  962.  
  963.     /* call callback for click */
  964.     if (bPtr->action)
  965.     (*bPtr->action)(bPtr, bPtr->clientData);
  966.  
  967.     oldItem = item;
  968. }
  969.  
  970.  
  971. static void
  972. listDoubleCallback(void *self, void *clientData)
  973. {
  974.     WMBrowser *bPtr = (WMBrowser*)clientData;
  975.     WMList *lPtr = (WMList*)self;
  976.     WMListItem *item;
  977.  
  978.     item = WMGetListSelectedItem(lPtr);
  979.     if (!item)
  980.     return;
  981.  
  982.     /* call callback for double click */
  983.     if (bPtr->doubleAction)
  984.     (*bPtr->doubleAction)(bPtr, bPtr->doubleClientData);
  985. }
  986.  
  987.  
  988. void
  989. WMLoadBrowserColumnZero(WMBrowser *bPtr)
  990. {
  991.     if (!bPtr->flags.loaded) {
  992.     /* create column 0 */
  993.     WMAddBrowserColumn(bPtr);
  994.  
  995.     loadColumn(bPtr, 0);
  996.  
  997.     /* make column 0 visible */
  998.     scrollToColumn(bPtr, 0, True);
  999.  
  1000.     bPtr->flags.loaded = 1;
  1001.     }
  1002. }
  1003.  
  1004.  
  1005. void
  1006. WMRemoveBrowserItem(WMBrowser *bPtr, int column, int row)
  1007. {
  1008.     WMList *list;
  1009.  
  1010.     if (column < 0 || column >= bPtr->usedColumnCount)
  1011.     return;
  1012.  
  1013.     list = WMGetBrowserListInColumn(bPtr, column);
  1014.  
  1015.     if (row < 0 || row >= WMGetListNumberOfRows(list))
  1016.     return;
  1017.  
  1018.     removeColumn(bPtr, column+1);
  1019.     if (bPtr->usedColumnCount < bPtr->maxVisibleColumns)
  1020.     scrollToColumn(bPtr, 0, True);
  1021.     else
  1022.     scrollToColumn(bPtr, bPtr->usedColumnCount-bPtr->maxVisibleColumns,
  1023.                True);
  1024.  
  1025.     WMRemoveListItem(list, row);
  1026. }
  1027.  
  1028.  
  1029. static void
  1030. listSelectionObserver(void *observerData, WMNotification *notification)
  1031. {
  1032.     WMBrowser *bPtr = (WMBrowser*)observerData;
  1033.     int column, item = (int)WMGetNotificationClientData(notification);
  1034.     WMList *lPtr = (WMList*)WMGetNotificationObject(notification);
  1035.  
  1036.     for (column = 0; column < bPtr->usedColumnCount; column++)
  1037.         if (bPtr->columns[column] == lPtr)
  1038.             break;
  1039.  
  1040.     /* this can happen when a list is being cleared with WMClearList
  1041.      * after the column was removed */
  1042.     if (column >= bPtr->usedColumnCount) {
  1043.     return;
  1044.     }
  1045.  
  1046.     if (item < 0)
  1047.         column--;
  1048.  
  1049.     bPtr->selectedColumn = column;
  1050. }
  1051.  
  1052.  
  1053. int
  1054. WMAddBrowserColumn(WMBrowser *bPtr)
  1055. {
  1056.     WMList *list;
  1057.     WMList **clist;
  1058.     char **tlist;
  1059.     int colY;
  1060.     int index;
  1061.  
  1062.  
  1063.     if (bPtr->usedColumnCount < bPtr->columnCount) {
  1064.     return bPtr->usedColumnCount++;
  1065.     }
  1066.  
  1067.     bPtr->usedColumnCount++;
  1068.  
  1069.     if (bPtr->flags.isTitled) {
  1070.     colY = TITLE_SPACING + bPtr->titleHeight;
  1071.     } else {
  1072.     colY = 0;
  1073.     }
  1074.  
  1075.     index = bPtr->columnCount;
  1076.     bPtr->columnCount++;
  1077.     clist = wmalloc(sizeof(WMList*)*bPtr->columnCount);
  1078.     tlist = wmalloc(sizeof(char*)*bPtr->columnCount);
  1079.     memcpy(clist, bPtr->columns, sizeof(WMList*)*(bPtr->columnCount-1));
  1080.     memcpy(tlist, bPtr->titles, sizeof(char*)*(bPtr->columnCount-1));
  1081.     if (bPtr->columns)
  1082.     wfree(bPtr->columns);
  1083.     if (bPtr->titles)
  1084.     wfree(bPtr->titles);
  1085.     bPtr->columns = clist;
  1086.     bPtr->titles = tlist;
  1087.  
  1088.     bPtr->titles[index] = NULL;
  1089.  
  1090.     list = WMCreateList(bPtr);
  1091.     WMSetListAction(list, listCallback, bPtr);
  1092.     WMSetListDoubleAction(list, listDoubleCallback, bPtr);
  1093.     WMSetListUserDrawProc(list, paintItem);
  1094.     WMAddNotificationObserver(listSelectionObserver, bPtr,
  1095.                   WMListSelectionDidChangeNotification, list);
  1096.  
  1097.     bPtr->columns[index] = list;
  1098.  
  1099.     WMResizeWidget(list, bPtr->columnSize.width, bPtr->columnSize.height);
  1100.     WMMoveWidget(list, (bPtr->columnSize.width+COLUMN_SPACING)*index, colY);
  1101.     if (COLUMN_IS_VISIBLE(bPtr, index))
  1102.     WMMapWidget(list);
  1103.  
  1104.     /* update the scroller */
  1105.     if (bPtr->columnCount > bPtr->maxVisibleColumns) {
  1106.     float value, proportion;
  1107.  
  1108.     value = bPtr->firstVisibleColumn
  1109.         /(float)(bPtr->columnCount-bPtr->maxVisibleColumns);
  1110.     proportion = bPtr->maxVisibleColumns/(float)bPtr->columnCount;
  1111.     WMSetScrollerParameters(bPtr->scroller, value, proportion);
  1112.     }
  1113.  
  1114.     return index;
  1115. }
  1116.  
  1117.  
  1118.  
  1119. static void
  1120. destroyBrowser(WMBrowser *bPtr)
  1121. {
  1122.     int i;
  1123.  
  1124.     for (i = 0; i < bPtr->columnCount; i++) {
  1125.     if (bPtr->titles[i])
  1126.         wfree(bPtr->titles[i]);
  1127.     }
  1128.     wfree(bPtr->titles);
  1129.  
  1130.     wfree(bPtr->pathSeparator);
  1131.  
  1132.     WMRemoveNotificationObserver(bPtr);
  1133.  
  1134.     wfree(bPtr);
  1135. }
  1136.  
  1137.  
  1138. static char*
  1139. createTruncatedString(WMFont *font, char *text, int *textLen, int width)
  1140. {
  1141.     int dLen = WMWidthOfString(font, ".", 1);
  1142.     char *textBuf = (char*)wmalloc((*textLen)+4);
  1143.  
  1144.     if (width >= 3*dLen) {
  1145.     int dddLen = 3*dLen;
  1146.     int tmpTextLen = *textLen;
  1147.  
  1148.     strcpy(textBuf, text);
  1149.     while (tmpTextLen
  1150.            && (WMWidthOfString(font, textBuf, tmpTextLen)+dddLen > width))
  1151.         tmpTextLen--;
  1152.     strcpy(textBuf+tmpTextLen, "...");
  1153.     *textLen = tmpTextLen+3;
  1154.     } else if (width >= 2*dLen) {
  1155.     strcpy(textBuf, "..");
  1156.     *textLen = 2;
  1157.     } else if (width >= dLen) {
  1158.     strcpy(textBuf, ".");
  1159.     *textLen = 1;
  1160.     } else {
  1161.     *textBuf = '\0';
  1162.     *textLen = 0;
  1163.     }
  1164.     return textBuf;
  1165. }
  1166.